#ifndef UUID_1341A0126C6E451DC5E10E870D727FB1
#define UUID_1341A0126C6E451DC5E10E870D727FB1

#include "../gvl/support/cstdint.hpp"
#include "../gvl/support/bits.hpp"

#include "color.h"

#include <assert.h>

#ifdef __cplusplus
extern "C" {
#endif

enum
{
	flip_pf_8880 = 0,
	flip_pf_8888 = 1,
	flip_pf_5650 = 2,
	flip_pf_8000 = 3,

	flip_pf_count = 4
};

typedef struct flip_unpacked_pixel_format_
{
	uint32_t mask[4];
	uint32_t cmask[4];
	uint32_t shift[4];
	uint32_t bitspp;
	uint32_t bytespp;
} flip_unpacked_pixel_format;

typedef flip_unpacked_pixel_format flip_upf;

GVL_INLINE void
flip_upf_init_null(flip_upf* f)
{
	f->mask[0] = 0;
	f->mask[1] = 0;
	f->mask[2] = 0;
	f->mask[3] = 0;
	
	f->cmask[0] = 0;
	f->cmask[1] = 0;
	f->cmask[2] = 0;
	f->cmask[3] = 0;
	
	f->shift[0] = 0;
	f->shift[1] = 0;
	f->shift[2] = 0;
	f->shift[3] = 0;
	
	f->bitspp = 0;
	f->bytespp = 0;
}

GVL_INLINE void
flip_upf_init_with(flip_upf* f, uint32_t rmask, uint32_t gmask, uint32_t bmask, uint32_t amask, uint32_t bytespp)
{
	f->mask[0] = rmask;
	f->mask[1] = gmask;
	f->mask[2] = bmask;
	f->mask[3] = amask;
	
	f->cmask[0] = rmask;
	f->cmask[1] = gmask & ~rmask;
	f->cmask[2] = bmask & ~gmask & ~rmask;
	f->cmask[3] = amask & ~bmask & ~gmask & ~rmask;
	
	f->shift[0] = 31 - gvl_log2(rmask);
	f->shift[1] = 31 - gvl_log2(gmask);
	f->shift[2] = 31 - gvl_log2(bmask);
	f->shift[3] = 31 - gvl_log2(amask);
	
	f->bitspp = 1 + gvl_top_bit(rmask | bmask | gmask | amask);
	f->bytespp = bytespp;
}

GVL_INLINE uint32_t
flip_upf_with_a(flip_upf const* f, uint32_t px, uint32_t a)
{
	return (px & ~f->mask[3]) | ((a << 24) >> f->shift[3]);
}

GVL_INLINE void
flip_upf_get_rgba(flip_upf const* f, uint32_t px, uint32_t* r, uint32_t* g, uint32_t* b, uint32_t* a)
{
	*r = ((px & f->mask[0]) << f->shift[0]) >> 24;
	*g = ((px & f->mask[1]) << f->shift[1]) >> 24;
	*b = ((px & f->mask[2]) << f->shift[2]) >> 24;
	*a = ((px & f->mask[3]) << f->shift[3]) >> 24;
}

GVL_INLINE uint32_t
flip_upf_rgba(flip_upf const* f, uint32_t r, uint32_t g, uint32_t b, uint32_t a)
{
	// We use the cummulative mask to avoid channel overlap.
	// This means e.g. that rgba -> grayscale will only use the red channel
	// when using this function.
	r = ((r << 24) >> f->shift[0]) & f->cmask[0];
	g = ((g << 24) >> f->shift[1]) & f->cmask[1];
	b = ((b << 24) >> f->shift[2]) & f->cmask[2];
	a = ((a << 24) >> f->shift[3]) & f->cmask[3];
	
	return (r + g + b + a);
}

GVL_INLINE uint32_t
flip_upf_color(flip_upf const* f, flip_color col)
{
	return flip_upf_rgba(f, flip_get_r(col), flip_get_g(col), flip_get_b(col), flip_get_a(col));
}

GVL_INLINE uint32_t
flip_convert_pixel(flip_upf const* from, uint32_t px, flip_upf const* to)
{
	uint32_t r, g, b, a;
	
	flip_upf_get_rgba(to, px, &r, &g, &b, &a);
	return flip_upf_rgba(from, r, g, b, a);
}

void flip_init_pf_table();

extern int flip_pf_table_initialized;
extern flip_upf flip_pf_table[flip_pf_count];

GVL_INLINE flip_upf const* flip_unpack_pf(uint32_t format)
{
	assert(format >= 0 && format < flip_pf_count);
	if(!flip_pf_table_initialized)
		flip_init_pf_table();
	return &flip_pf_table[format];
}

#ifdef __cplusplus
} // extern "C"
#endif

#endif // UUID_1341A0126C6E451DC5E10E870D727FB1
